/* gm_sim_glutstuff.c -- Funções de desenho do simulador
 * 
 * Copyright (C) 2008, Gustavo Campos Martins <>
 * GEAR - Grupo de Estudos Avançados em Robótica
 * <http://www.sel.eesc.usp.br/gear>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <stdio.h>
#include <math.h>
#include <pthread.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glu.h>
#include <GL/glut.h>

#include "gm_sim.h"
#include "gm_sim_utils.h"
#include "gm_sim_glutstuff.h"
#include "gm_sim_physics.h"


/** funções **/
static void gm_sim_glut_display( void );
static void gm_sim_glut_drawCircle(int ,int ,float );
static void gm_sim_glut_drawField( void );
static void gm_sim_glut_drawRobot( int );
static void gm_sim_glut_initGL( void );
static void gm_sim_glut_mouseMotion( int, int );
static void gm_sim_glut_processMouse( int, int, int, int );
static void * gm_sim_glut_thread(  );
static void gm_sim_glut_timercall( int );

/* Ponteiro para os dados do jogo */ 
extern gm_sim_t * self;


/** Inicializa a thread do glut.
 *
 * @param *self Estrutura que contém os dados do simulador
 */
void
gm_sim_glut_init( )
{
	self->mouseState = 0;
	self->placar_a = 0;
	self->placar_b = 0;

	pthread_t p;
	pthread_create(&p, NULL, (void *) gm_sim_glut_thread, NULL);
}

/** Thread do glut.
 *
 * @param *self Estrutura que contém os dados do simulador
 */
void *
gm_sim_glut_thread( )
{
	int argc = 0;
	char *argv[] = { "" };

	glutInit(&argc, argv);

	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);

	glutInitWindowSize(840, 640);
	glutCreateWindow("GEARsim");

	gm_sim_glut_initGL();

	glutDisplayFunc(gm_sim_glut_display);
	glutTimerFunc(SLEEP_TICKS, gm_sim_glut_timercall, 0);

	glutMotionFunc(gm_sim_glut_mouseMotion); 
	glutMouseFunc(gm_sim_glut_processMouse);

	glutMainLoop();
}

/** 
 * Inicializa o OpenGL
 */
void
gm_sim_glut_initGL( void )
{
	glClearColor(0.271, 0.765, 0.102, 0.0);

	glPointSize(3.0);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-1000.0f, 1600.0f, -1000.0, 1000.0, -1.0, 1.0);
	glScalef(2.0, 2.0, 1.0);
	glMatrixMode(GL_MODELVIEW);

	/*gm_sim_glut_buildFont();*/ /*TODO*/ 
}

/**
 * Faz os desenhos na tela.
 */
static void
gm_sim_glut_display( void )
{
	glClear(GL_COLOR_BUFFER_BIT);

	glLoadIdentity();

	glTranslatef(-500.0f, -500.0f, 0.0f);

	/* desenha o campo */
	gm_sim_glut_drawField();

	/* desenha os robôs */
	for (int i = 0; i < NUM_ROB; ++i)
		gm_sim_glut_drawRobot(i);
	
	/* desenha a bola */
	glColor3f(0.9f, 0.7f, 0.2f);
	gm_sim_glut_drawCircle((int) self->ball->p.x, (int) self->ball->p.y, BALL_RADIUS);

	glutSwapBuffers();

	gm_sim_phys_update();
}

/** Desenha o robô
 *
 * @param num identificador do robô 
 */
static void 
gm_sim_glut_drawRobot( int num )
{
	int x = self->robot[num]->p.x;
	int y = self->robot[num]->p.y;

	float i;
	float alfa;

	glColor3f(0.0f, 0.0f, 0.0f);
	glBegin(GL_POLYGON);
	for (i=35+self->robot[num]->a*RAD2DEG; i<326+self->robot[num]->a*RAD2DEG; i++) {
		alfa = i*DEG2RAD;
		glVertex2f(x + cos(alfa)*ROBOT_RADIUS, y + sin(alfa)*ROBOT_RADIUS);
	}
	glEnd();
	
	if (num < 5) 
		glColor3f(0.2784f, 0.6431f, 0.7765f);
	else 
		glColor3f(0.9f, 0.1f, 0.1f);
	gm_sim_glut_drawCircle(x - (BALL_RADIUS*3.0/2.0)*cos(self->robot[num]->a),
			       y - (BALL_RADIUS*3.0/2.0)*sin(self->robot[num]->a),
			       BALL_RADIUS);
	
	if (num < 5)
		glColor3f(0.2*num, 1.0 - 0.2*num, 0.4);
	else 
		glColor3f(0.2*(num-5), 1.0 - 0.2*(num-5), 0.4);
	gm_sim_glut_drawCircle(x + (BALL_RADIUS*3.0/2.0)*cos(self->robot[num]->a),
			       y + (BALL_RADIUS*3.0/2.0)*sin(self->robot[num]->a),
			       BALL_RADIUS);
}

/**
 * Timercall do glut
 */
void gm_sim_glut_timercall(int value)
{
        glutTimerFunc(SLEEP_TICKS, gm_sim_glut_timercall, 0);
                
        glutPostRedisplay();
}

/**
 * Desenha um círculo.
 *
 * @param x Coordenada X do centro do círculo.
 *
 * @param y Coordenada Y do centro do círculo.
 *
 * @param r Raio do cïrculo.
 */
void
gm_sim_glut_drawCircle(int x, int y, float r)
{
	int i;
	float alfa;

	glBegin(GL_POLYGON);

	for (i=0; i<360; i++) {
		alfa = i*DEG2RAD;
		glVertex2f(x + cos(alfa)*r, y + sin(alfa)*r);
	}

	glEnd();
}

/**
 * Desenha o campo
 */
static void
gm_sim_glut_drawField( void )
{
	glColor3f(1.0f, 1.0f, 1.0f);
	
	glBegin(GL_LINE_LOOP);
		glVertex2f(FIELD_BR.x, FIELD_BR.y);
		glVertex2f(GOALOPP_B.x, GOALOPP_B.y);
		glVertex2f(WORLD_BR.x, GOALOPP_B.y);
		glVertex2f(WORLD_BR.x, GOALOPP_U.y);
		glVertex2f(GOALOPP_U.x, GOALOPP_U.y);
		glVertex2f(FIELD_UR.x, FIELD_UR.y);
		glVertex2f(FIELD_UL.x, FIELD_UL.y);
		glVertex2f(GOALOUR_U.x, GOALOUR_U.y);
		glVertex2f(WORLD_BL.x, GOALOUR_U.y);
		glVertex2f(WORLD_BL.x, GOALOUR_B.y);
		glVertex2f(GOALOUR_B.x, GOALOUR_B.y);
		glVertex2f(FIELD_BL.x, FIELD_BL.y);
	glEnd();

	glBegin(GL_LINES);
		glVertex2f( (FIELD_BR.x + FIELD_BL.x)/2, FIELD_BR.y);
		glVertex2f( (FIELD_BR.x + FIELD_BL.x)/2, FIELD_UR.y);
	glEnd();

	float r = (WORLD_BR.x - WORLD_BL.x)*0.164/2;
	glBegin(GL_LINE_LOOP);
		for (int i=0; i<360; i++) {
			glVertex2f(cos(i*DEG2RAD)*r + (WORLD_BR.x + WORLD_BL.x)/2, 
				   sin(i*DEG2RAD)*r + (WORLD_BR.y + WORLD_UR.y)/2);
		}
	glEnd();
}

/** Cuida das funções drag and drop
 *
 */
void gm_sim_glut_mouseMotion(int x, int y) 
{
	//transforma coordenadas da tela em coordenadas do opengl
	float tx = 1000.0*x/640.0;
	float ty = 1000.0*(640.0 - y)/640.0; 
	
	if (self->mouseState == 1) {
		self->ball->p.x = tx + self->dx;
		self->ball->p.y = ty + self->dy;
		self->ball->v = cpvzero;
	}
	else if (self->mouseState != 0) {
	printf("%d\n", self->mouseState-2);
		self->robot[self->mouseState-2]->p.x = tx + self->dx;
		self->robot[self->mouseState-2]->p.y = ty + self->dy;
	}
}

/**
 * Função para capturar a posição do mouse
 **/
void gm_sim_glut_processMouse(int button, int state, int x, int y) {
	//transforma coordenadas da tela em coordenadas do opengl
	float tx = 1000.0*x/640.0;
	float ty = 1000.0*(640.0 - y)/640.0;

	/*
	if ( (state == GLUT_DOWN) && (button == GLUT_LEFT_BUTTON) ) {
		//transforma coordenadas da tela em coordenadas do opengl
		ball->p.x = (float) 1000*x/640.0; 
		ball->p.y = (float) 1000*(640 - y)/640.0;
		//ball->v = cpvzero;
	}
	*/

	if ( (state == GLUT_DOWN) && (button == GLUT_LEFT_BUTTON) ) {
		//se está em cima da bola
		if (sqrt( pow(self->ball->p.x - tx, 2) + pow(self->ball->p.y - ty, 2) ) <= BALL_RADIUS + 10.0) {
			self->mouseState = 1;
			self->dx = self->ball->p.x - tx;
			self->dy = self->ball->p.y - ty;
		}
		//se está em cima do robô
		else {
			int i = 0;
			for (i = 0; i < NUM_ROB; ++i) {
				if (pow(self->robot[i]->p.x - tx, 2) + pow(self->robot[i]->p.y - tx, 2) <= 
				    ROBOT_RADIUS*ROBOT_RADIUS) {
					self->mouseState = i + 2;
					self->dx = self->robot[i]->p.x - tx;
					self->dy = self->robot[i]->p.y - ty;
					break;
				}
			}
		}
	}

	if ( (state == GLUT_UP) && (button == GLUT_LEFT_BUTTON) ) {
		self->mouseState = 0;
	}

	if ( (state == GLUT_DOWN) && (button == GLUT_RIGHT_BUTTON) ) {
		self->ball->v = cpvmult(cpvsub(cpv(tx, ty), self->ball->p), 0.5);
	}


}
